MAF1731 - Microstructure and Trading Systems

B.Eng Financial Engineering



I.F. Andrés González Luna Díaz del Castillo

I.F. Andrés Rámirez Villanueva

Apr 2023 | Repository: Link


Lab 4


1. Introduction


En este laboratorio usaremos información del Libro de Ordenes de Criptomonedas de diversos exchanges internacionales, con el objetivo primario de visualizar y modelar la microestructura del mercado por medio del comportamiento de Libro de Ordenes y sus caracteristicas como; Matching Engine, Levels, Ask-Bid Volumnes, etc. 


2. Objetivos

Utilizando la libreria ccxt de python para consultar información del Libro de Ordenes (LO) de criptomonedas de diversos exchanges, extraer; ask, ask-volume, bid, bid-volume y spread; construir las series de tiempo: 

  • Ask-volume para cada timestamp del LO.
  • Bid-volume para cada timestamp del LO.
  • Total Volume (ask-bid) para cada timestamp del LO. 
  • Mid-Price (promedio de Top Of the Book) para cada timestamp del LO.
  • VWAP para cada timestamp del LO. 

Graficar usando plotly para python cada una de ellas y para cada uno de los exchanges.

Install/Load Packages and Depedencies


In [1]:
import data
import pandas as pd
import functions
import plotly.express as px
import visualizations
import numpy as np

2.2 Python Packages

In order to run this notebook, it is necessary to have installed and/or have the requirements.txt file with the following:

  • pandas>=1.1.1
  • numpy>=1.19.1
  • jupyter>=1.0.0

2.3 Files Dependencies

The following are the file dependencies that are needed to run this notebook:

  • files/precios.csv : Precios históricos
  • files/datos.txt : Otros datos históricos

2.3 Install Packages

In [2]:
%%capture

# Install all the pip packages in the requirements.txt
import sys
!{sys.executable} -m pip install -r requirements.txt


3. Data Description


Para descargar la información sobre las cryptos utilizamos la API de CCXT. Descargamos los trading books de las siguientes plataformas: kucoin, bittrex, y bitfinex. Utilizamos dos monedas: BTCUSDT y ETHEUR. Para cada una de las monedas tenemos las ordenes de compra y venta de las plataformas mencionadas anteriormente. La información fue descargada durante una hora. Puede ser que tengamos algunos brincos en la data ya que la API tenía fallas por lo que durante la hora que estuvo descargando, la conexión falló.

In [3]:
#df1 = pd.read_json('files\orderbooks_26abr2023_BTCUSDT.json')
df1 = pd.read_json('files\orderbooks_27abr2023_BTCUSDT.json')
df1.head()
Out[3]:
exchange datetime level bid_volume ask_volume total_volume mid_price spread close_price Symbol
0 kucoin 2023-04-27 15:13:18.499000+00:00 100 32.359104 45.331784 77.690888 29011.95 0.1 28894.0 BTC/USDT
1 kucoin 2023-04-27 15:13:19.096000+00:00 100 34.657877 44.589152 79.247029 29011.95 0.1 28894.0 BTC/USDT
2 kucoin 2023-04-27 15:13:19.690000+00:00 100 39.448490 45.330344 84.778834 29011.95 0.1 28894.0 BTC/USDT
3 kucoin 2023-04-27 15:13:20.254000+00:00 100 39.448490 45.330341 84.778831 29011.95 0.1 28894.0 BTC/USDT
4 kucoin 2023-04-27 15:13:20.847000+00:00 100 38.110407 45.763440 83.873847 29011.95 0.1 28894.0 BTC/USDT
In [4]:
#df2 = pd.read_json('files\orderbooks_26abr2023_ETHEUR.json')
df2 = pd.read_json('files\orderbooks_27abr2023_ETHEUR.json')
df2.head()
Out[4]:
exchange datetime level bid_volume ask_volume total_volume mid_price spread close_price Symbol
0 kucoin 2023-04-27 14:00:57.708000+00:00 100 65.8326 20.7654 86.5980 1720.62 39.18 1697.1 ETH/EUR
1 kucoin 2023-04-27 14:00:58.293000+00:00 100 66.5880 20.7654 87.3534 1720.62 39.18 1697.1 ETH/EUR
2 kucoin 2023-04-27 14:00:58.864000+00:00 100 65.5883 20.7460 86.3343 1720.62 39.18 1697.1 ETH/EUR
3 kucoin 2023-04-27 14:00:59.501000+00:00 100 61.0104 20.7460 81.7564 1720.62 39.18 1697.1 ETH/EUR
4 kucoin 2023-04-27 14:01:00.077000+00:00 100 67.6853 20.7460 88.4313 1720.62 39.18 1697.1 ETH/EUR


4. Procesos


VWAP

In [5]:
df1 = functions.vwap(df1)
df2 = functions.vwap(df2)

df = pd.concat([df1, df2])
df[['Symbol','exchange', 'datetime', 'level', 'ask_volume', 'bid_volume', 'total_volume', 'mid_price', 'VWAP']]
Out[5]:
Symbol exchange datetime level ask_volume bid_volume total_volume mid_price VWAP
0 BTC/USDT kucoin 2023-04-27 15:13:18.499000+00:00 100 45.331784 32.359104 77.690888 29011.95 29011.950000
1 BTC/USDT kucoin 2023-04-27 15:13:19.096000+00:00 100 44.589152 34.657877 79.247029 29011.95 29011.950000
2 BTC/USDT kucoin 2023-04-27 15:13:19.690000+00:00 100 45.330344 39.448490 84.778834 29011.95 29011.950000
3 BTC/USDT kucoin 2023-04-27 15:13:20.254000+00:00 100 45.330341 39.448490 84.778831 29011.95 29011.950000
4 BTC/USDT kucoin 2023-04-27 15:13:20.847000+00:00 100 45.763440 38.110407 83.873847 29011.95 29011.950000
... ... ... ... ... ... ... ... ... ...
2631 ETH/EUR bitfinex 2023-04-27 15:03:41.477000+00:00 25 247.647766 200.102944 447.750710 1716.00 1716.299037
2632 ETH/EUR bitfinex 2023-04-27 15:04:07.519000+00:00 25 254.021126 215.496470 469.517596 1716.75 1716.302060
2633 ETH/EUR bitfinex 2023-04-27 15:04:34.084000+00:00 25 281.909769 204.346181 486.255950 1716.90 1716.306182
2634 ETH/EUR bitfinex 2023-04-27 15:05:00.173000+00:00 25 265.617476 204.755216 470.372692 1716.85 1716.309785
2635 ETH/EUR bitfinex 2023-04-27 15:05:26.231000+00:00 25 319.873814 198.631875 518.505689 1717.35 1716.317327

5522 rows × 9 columns

La funcion de VWAP saca el Volume Weighted Average Price (VWAP). Es una métrica finacniera utilizada por traders para calcular el precio promedio de un activo sobre cierto periodo de tiempo. Toma en cuenta tanto el precio como el volumen de trades.

VWAP Visualizations

Levels Bid volume Ask volume Total volume Mid price VWAP

BTC/USDT

In [6]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[0]], x="datetime", y="level", color='exchange', title=list(set(df['Symbol']))[0]+" level over period")
fig.show()
In [7]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[0]], x="datetime", y="bid_volume", color='exchange', title=list(set(df['Symbol']))[0]+" bid volume over period")
fig.show()
In [8]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[0]], x="datetime", y="ask_volume", color='exchange', title=list(set(df['Symbol']))[0]+" ask volume over period")
fig.show()
In [9]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[0]], x="datetime", y="total_volume", color='exchange', title=list(set(df['Symbol']))[0]+" total volume over period")
fig.show()
In [10]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[0]], x="datetime", y="mid_price", color='exchange', title=list(set(df['Symbol']))[0]+" mid prices over period")
fig.show()
In [11]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[0]], x="datetime", y="VWAP", color='exchange', title=list(set(df['Symbol']))[0]+" VWAP over period")
fig.show()

Como podemos observar, en algunas de las gráficas podemos ver una volatilidad bastante representativa, a su vez donde hay más movimiento es en kucoin, en cuanto a los mid prices no hay tanta diferencia.

ETH/EUR

In [12]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[1]], x="datetime", y="level", color='exchange', title=list(set(df['Symbol']))[1]+" level over period")
fig.show()
In [13]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[1]], x="datetime", y="bid_volume", color='exchange', title=list(set(df['Symbol']))[1]+" bid volume over period")
fig.show()
In [14]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[1]], x="datetime", y="ask_volume", color='exchange', title=list(set(df['Symbol']))[1]+" ask volume over period")
fig.show()
In [15]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[1]], x="datetime", y="total_volume", color='exchange', title=list(set(df['Symbol']))[1]+" total volume over period")
fig.show()
In [16]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[1]], x="datetime", y="mid_price", color='exchange', title=list(set(df['Symbol']))[1]+" mid prices over period")
fig.show()
In [17]:
fig = px.line(df[df['Symbol'] == list(set(df['Symbol']))[1]], x="datetime", y="VWAP", color='exchange', title=list(set(df['Symbol']))[1]+" VWAP over period")
fig.show()

Contrario al caso anterior, hay mayor diferencia en VWAP y mayor movimiento en otras plataformas, también mucha diferencia en los mid prices.

3.2 Rolls Effective Spread

In [18]:
df_rolls1 = functions.rolls(df1, 30) 
df_rolls2 = functions.rolls(df2, 30)

Rolls's Effective Spread es una medida del valor real del costo de transaccion de un activo. Toma en cuenta tanto los costos explicitos como los implicitos. Richar Roll propuse esta medida en 1984, por lo que lleva su nombre. Nosotros utilizamos un lag de 30 minutos porque nuestros closing prices los teníamos por minuto, y asi teníamos un Effective Spread más visible. En la función se puede ajustar al Lag que uno quiera.

BTC/USDT

Kucoin

In [19]:
df_rolls1[0][['close_price', 'spread', 'Rolls Spread']]
Out[19]:
close_price spread Rolls Spread
Time
2023-04-27 15:00:00+00:00 28900.877824 0.430680 NaN
2023-04-27 15:30:00+00:00 28987.424110 0.294425 0.000537
2023-04-27 16:00:00+00:00 29023.339638 0.152213 0.000641

Bittrex

In [20]:
df_rolls1[1][['close_price', 'spread', 'Rolls Spread']]
Out[20]:
close_price spread Rolls Spread
Time
2023-04-27 15:00:00+00:00 29761.371871 54.383771 NaN
2023-04-27 15:30:00+00:00 29789.112822 52.845746 0.000755
2023-04-27 16:00:00+00:00 29809.829913 44.330060 0.000111

Bitfnex

In [21]:
df_rolls1[2][['close_price', 'spread', 'Rolls Spread']]
Out[21]:
close_price spread Rolls Spread
Time
2023-04-27 15:00:00+00:00 3870.1 4.656863 NaN
2023-04-27 15:30:00+00:00 3870.1 5.288889 0.0
2023-04-27 16:00:00+00:00 3870.1 2.986667 0.0

ETH/EUR

Kucoin

In [22]:
df_rolls2[0][['close_price', 'spread', 'Rolls Spread']]
Out[22]:
close_price spread Rolls Spread
Time
2023-04-27 14:00:00+00:00 1701.498000 38.977331 NaN
2023-04-27 14:30:00+00:00 1721.232812 38.613941 0.001526
2023-04-27 15:00:00+00:00 1738.446667 38.045035 0.002521

Bittrex

In [23]:
df_rolls2[1][['close_price', 'spread', 'Rolls Spread']]
Out[23]:
close_price spread Rolls Spread
Time
2023-04-27 14:00:00+00:00 1744.618138 27.568642 NaN
2023-04-27 14:30:00+00:00 1742.074000 25.999024 0.0
2023-04-27 15:00:00+00:00 1742.074000 22.850736 0.0

Bitfnex

In [24]:
df_rolls2[2][['close_price', 'spread', 'Rolls Spread']]
Out[24]:
close_price spread Rolls Spread
Time
2023-04-27 14:00:00+00:00 327.612273 0.429885 NaN
2023-04-27 14:30:00+00:00 327.612273 0.448333 0.0
2023-04-27 15:00:00+00:00 327.612273 0.566667 0.0

5. Conclusiones


Como es evidente, en algumas plataformas hay más volatilidad que en otras, esto se puede deber a varios factores ajenos a la criptomoneda, desde el horario en que se descargó la información hasta las comisiones que tiene cada una. En este laboratorio no sólo se nos enseñó a analizar desde otra perspectiva a las criptos sino a utilizar herramientas que nunca habíamos utilizado, el hecho de que usemos plotly le da un valor agregado a cualquier trabajo que entreguemos. Uno de los inconvenientes que tuvimos fue la descarga de datos, era muy inestable, afortunadamente se solucionó por medio de código.